import abc
import builtins
import json
from urllib import request, parse
import os.path
import chardet

MIME_LIST = json.load(open("MIME.json", "r"))
BASE_PATH = './ServerData'
ENCODE = "GB2312"


# 获取文件编码类型
def get_encoding(file):
    # 二进制方式读取，获取字节数据，检测类型
    with open(file, 'rb') as f:
        _data = f.read()
        return chardet.detect(_data)['encoding']


class HttpRequestHeader:
    """
    头部分析 目前只分析了content类型 content长度，目的地址，请求方法
    """

    def __init__(self, header_bytes: bytes):
        self.header = {}
        header_str = header_bytes.decode()
        header_str = header_str.split('\r\n\r\n')[0]
        header_list = header_str.split('\r\n')
        self.method = header_list[0].split()[0]

        _url = header_list[0].split()[1].split('?')
        self.path = _url[0]
        self.params = parse.parse_qs("?".join(_url[1:]))
        self.version = header_list[0].split()[2]

        for index, header_ele in enumerate(header_list):
            if index == 0:
                continue
            if ': ' in header_ele:
                if header_ele.split(': ')[0] != 'Content-Type':
                    self.header[header_ele.split(': ')[0]] = header_ele.split(': ')[1]
                else:
                    content_type = header_ele.split('; ')[0]
                    self.header[content_type.split(': ')[0]] = content_type.split(': ')[1]
                    if self.header['Content-Type'] == 'multipart/form-data':
                        boundary = header_ele.split('; ')[1]
                        self.header[boundary.split('=')[0]] = boundary.split('=')[1]

    def __str__(self):
        basic = ' '.join([self.method,
                          self.path,
                          self.version])
        other = json.dumps(self.header, indent=4)
        return basic + '\n' + "params:" + str(self.params) + "\n" + other


class ResponseData:
    """
    抽象类，用来填写头部信息的Content-type和length等信息
    继承并实现子类以处理更多的返回类型
    """

    @abc.abstractmethod
    def get_head_ele(self):
        """
        获取头相关内容
        :return: ["Content-Type: */*", "Content-length: 123"]
        """
        pass

    @abc.abstractmethod
    def get_content_bytes(self):
        """
        content 按照url标准用%编码后的数据
        :return: bytes
        """
        pass


class JsonResponseData(ResponseData):
    """
    json 类型的返回数据
    """
    __CONTENT_TYPE = "Content-Type: application/json"

    def __init__(self, data=None, json_str=""):
        """
        有两种声明方式，json_str或data,若有data则json_str无效
        :param json_str: json.dumps 结果，index=0
        :param data: dict字典
        """
        if data:
            json_str = json.dumps(data)
        self.__data_bytes = json_str.encode()

    def get_head_ele(self):
        return [JsonResponseData.__CONTENT_TYPE]

    def get_content_bytes(self):
        print(self.__data_bytes)
        return self.__data_bytes


class FileResponseData(ResponseData):
    """
    文件类型的返回数据，在MIME.json中寻找匹配的类型
    """

    def __init__(self, file_path: str):
        try:
            file_path = BASE_PATH + file_path
            encode = get_encoding(file_path)
            print("file name: ", file_path, "\nencode type: ", encode)
            read_type = "r"
            if encode is None:
                read_type = "rb"
            with open(file_path, read_type, encoding=encode) as file:
                file_name = os.path.basename(file_path)
                if '.' in file_name:
                    file_type = '.' + file_name.split('.')[-1]
                    if file_type in MIME_LIST.keys():
                        self.__contest_type = "Content-Type: " + MIME_LIST[file_type]
                        self.__file_data = file.read()
                        if encode is not None:
                            self.__file_data = self.__file_data.encode()
                        self.__content_len = "Content-length: " + str(len(self.__file_data))
                        return
                print("无法处理的文件类型")
                raise RuntimeError("无法处理的文件类型")
        except FileNotFoundError:
            print("未找到文件")
            raise FileNotFoundError(file_path)

    def get_head_ele(self):
        return [self.__contest_type, self.__content_len]

    def get_content_bytes(self):
        return self.__file_data


class HttpResponseMaker:
    """
    响应体构造，包括头填写，响应数据编码等
    """
    VERSION = "HTTP/1.1"
    SERVER_NAME = "My-Easy-Server/0.1"

    def __init__(self, _code, _status: str, _data: ResponseData = None):
        header = list()
        header.append(" ".join([HttpResponseMaker.VERSION, str(_code), _status]))
        header.append("Server: " + HttpResponseMaker.SERVER_NAME)
        content = "".encode()
        if _data:
            header = header + _data.get_head_ele()
            content = _data.get_content_bytes()
        header = '\r\n'.join(header)
        header = header.encode()
        self.__response = '\r\n\r\n'.encode().join([header, content])

    def get_response(self):
        return self.__response


class ContentParser:
    """
    消息体分析类，用来分析请求中的content 目前只实现了urlencoded json两种类型
    """

    def __init__(self, _type, _bytes):
        self.__bytes = _bytes
        self.__data = None
        if _type == "application/x-www-form-urlencoded":
            self.urlencoded_parser()
        elif _type == "application/json":
            self.json_parser()

    def urlencoded_parser(self):
        self.__data = parse.parse_qs(self.__bytes.decode())

    def json_parser(self):
        self.__data = json.loads(self.__bytes)

    def get_data(self):
        return self.__data


if __name__ == '__main__':
    try:
        data = FileResponseData("MIME.json")
        print(HttpResponseMaker(_data=data, _code=200, _status="OK").get_response())
    except Exception as e:
        print(e.with_traceback())
